from django.contrib.auth.decorators import login_required, permission_required
from django.http import Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse 
from django.conf import settings
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.db.models import Q
from django.forms.models import modelformset_factory, inlineformset_factory
from mptt.forms import *
from django.shortcuts import redirect
from django.template import RequestContext

from tj3.models import * 
from settings import * 
import datetime 
from calendar import Calendar 
from itertools import groupby
from string import capwords

##################################################
# projects
##################################################
from django.shortcuts import render_to_response
from django.utils.safestring import mark_safe
from utility import *
from TJreport import *
import os.path

TASK_COLUMNS=['bsi','id',
    'scenario','start','end','duration','complete','status','pathcriticalness',]
RESOURCE_COLUMNS=['bsi','id','scenario','complete','effort','effortdone','effortleft','fte','daily{}']
    
def task_manage (request):
	params={}
	params['select_project']=ProjectSelectForm()
	
	if request.method=='POST':
		params['project']=proj=Project.objects.get(id=int(request.POST['project_list']))

		params['tasks']=[]
		for t in Task.tree.filter(project=proj):
			# periods
			tp = TaskPeriod.objects.filter(task=t)
			
			# resources
			rr=[]
			for ar in AllocateResource.objects.filter(task=t):
				rr.append(ar.primary)
				if ar.alternative: rr.append(ar.alternative)
			rr=list(set(rr))
			
			# dependencies
			dep = []
			for d in TaskDependency.objects.filter(from_task=t):
				pre='%d'%d.to_task.id
				
				if d.type == 'depends' and d.on_what=='onstart': # SS
					pre+='SS'
				elif d.type=='depends' and d.on_what=='onend': # SF
					pre+='SF'
				elif d.type =='precedes' and d.on_what=='onstart': # FS
					pre+='FS'
				elif d.type =='precedes' and d.on_what=='onend': # FF
					pre+='FF'
				
				if d.gap_duration: pre += d.gap_duration
				elif d.gap_length: pre+= d.gap_length
				dep.append(pre)
			
			# worklimits
			wl = [r.work_limit for r in rr]
			
			params['tasks'].append((t,tp,'\n'.join(dep)))
	
	return render_to_response('tj3/task_manage.html',params)


def project_compile (request):
	params={}
	params['select_project']=ProjectSelectForm()
	
	if request.method=='POST':
		params['project']=proj=Project.objects.get(id=int(request.POST['project_list']))
		ProjectTaskEmail(proj)
		
		# time period
		t =[('1w','1w')] # will be replaced by DB later
		f = ['html']
		DumpReport (proj, columns=TASK_COLUMNS, type='taskreport', time_period=t, formats=f)
		DumpReport (proj, columns=RESOURCE_COLUMNS, type='resourcereport', time_period=t, formats=f)
		
		# timesheet
		DumpTimesheet(proj)
			
		return redirect('resource_display')
	else:
		return render_to_response('tj3/project_compile.html',params)


def timeoff_create(request,resource_id):
	params={}
	params['resource']=resource=Resource.objects.get(id=int(resource_id))	
	TimeoffFormset=modelformset_factory(TimeOff,extra=1,exclude=('resource',))

	if request.method=='POST':
		new_timeoff=TimeoffFormset(request.POST)
		if new_timeoff.is_valid(): 
			t=new_timeoff.save(commit=False)[0]
			t.resource=resource
			t.save()
		return redirect('resource_display')
	else:
		params['timeoffs']=TimeOff.objects.filter(resource=resource).order_by('-end')
		params['timeoff_formset']=TimeoffFormset(queryset=TimeOff.objects.none())
		return render_to_response('tj3/timeoff_create.html',params)	

def resource_allocate_edit(request, alloc_id):
	params={}
	alloc=AllocateResource.objects.filter(id=int(alloc_id))
	AllocateFormset=modelformset_factory(AllocateResource,extra=0)
	if request.method=='POST':
		new_alloc=AllocateFormset(request.POST)
		cmd=request.POST['command'].lower()
		
		if cmd =='update' and new_alloc.is_valid():
			new_alloc.save()
		elif cmd =='delete':
			alloc[0].delete()
		return redirect('resource_display')
	else:
		params['alloc_formset']=AllocateFormset(queryset=worklimit)
	return render_to_response('tj3/resource_allocate_edit.html',params)
	
def worklimit_edit(request, wl_id):
	params={}
	worklimit=WorkLimit.objects.filter(id=int(wl_id))
	WorklimitFormset=modelformset_factory(WorkLimit,extra=0)
	if request.method=='POST':
		new_worklimit=WorklimitFormset(request.POST)
		cmd=request.POST['command'].lower()
		
		if cmd =='update' and new_worklimit.is_valid():
			new_worklimit.save()
		elif cmd =='delete':
			worklimit.delete()
		return redirect('resource_display')
	else:
		params['worklimit_formset']=WorklimitFormset(queryset=worklimit)
	return render_to_response('tj3/worklimit_edit.html',params)
	
def resource_create(request):
	params={}
	ResourceFormset=modelformset_factory(Resource,extra=1)
	WorklimitFormset=modelformset_factory(WorkLimit,extra=1)

	# get allocated resource
	if request.method=='POST':
		new_resource=ResourceFormset(request.POST,prefix='resource')
		new_worklimit=WorklimitFormset(request.POST,prefix='worklimit')
		
		if new_resource.is_valid():
			r=new_resource.save(commit=False)[0]
			
		# if we definie a new worklimit
		# assuming that this should be used by this resource	
		if new_worklimit.is_valid():
			w = new_worklimit.save()
			if len(w):
				r.work_limit=w[0]
		
		# save resource & resource taggs
		r.save()
		new_resource.save_m2m()
				
		# auto create an AllocateResouce
		# the whole purpose of creating a resource is to use him.
		new_alloc=AllocateResource()
		new_alloc.primary=r
		new_alloc.save()
		return redirect('resource_display')	
	else:
		params['resource_formset']=ResourceFormset(queryset=Resource.objects.none(),prefix='resource')
		params['worklimit_formset']=WorklimitFormset(queryset=WorkLimit.objects.none(),prefix='worklimit')
		return render_to_response('tj3/resource_create.html',params)	

def resource_edit(request, resource_id):
	params={}
	params['resource']=resource=Resource.objects.filter(id=int(resource_id))
	ResourceFormset=modelformset_factory(Resource,extra=0)
	if request.method=='POST':
		new_resource=ResourceFormset(request.POST)
		cmd=request.POST['command'].lower()
		
		if cmd =='update' and new_resource.is_valid():
			new_resource.save()
		elif cmd =='delete':
			resource.delete()
		elif cmd == 'create':
			return redirect('resource_create')
		return redirect('resource_display')
	else:
		params['resource_formset']=ResourceFormset(queryset=resource)
	return render_to_response('tj3/resource_edit.html',params)
	
def resource_allocate(request, task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))		
	params['resource_assigned'] = AllocateResource.objects.filter(task=task)
	params['resource_available']=AllocateResource.objects.all().exclude(task=task)
	AllocateResourceFormset=modelformset_factory(AllocateResource, exclude=('task',),extra=1)
	
	if request.method=='POST':
		cmd = request.POST['command'].lower()
		
		if cmd == 'create':
			# if we created a new allocate_resource, wow!
			new_alloc=AllocateResourceFormset(request.POST)
			if new_alloc.is_valid(): 
				alloc=new_alloc.save()[0]
				alloc.task.add(task)
				alloc.save()
		elif cmd == 'update':
			# figure out which resource is assigned to this task
			# we have to loop through all resource and check whose name
			# is in the request.POST
			for a in AllocateResource.objects.all():
				if str(a.id) in request.POST:
					a.task.add(task)
				else:
					try: # if it was assigned, uncheck
						a.task.remove(task)
					except: # if it wasn't assigned after all, do nothing.
						pass	
		return redirect('task_detail',task_id=task.id)
	else:
		params['alloc_formset']=AllocateResourceFormset(queryset=AllocateResource.objects.none())
		return render_to_response('tj3/resource_allocate.html',params)
	
def dep_edit (request, dep_id):
	params={}
	params['dep']=dep=TaskDependency.objects.filter(id=int(dep_id))
	params['task']= Task.objects.get(id=dep[0].from_task.id)
	DepFormset=modelformset_factory(TaskDependency,extra=0)
	
	if request.method=='POST':
		new_dep=DepFormset(request.POST)
		if request.POST['command'].lower()=='update' and new_dep.is_valid():
			new_dep.save()
		elif request.POST['command'].lower()=='delete':
			params['dep'].delete()
		return redirect('task_detail',task_id=params['task'].id)
	else:
		params['dep_formset']=DepFormset(queryset=dep)
		
	return render_to_response('tj3/dep_edit.html',params)
	
def dep_create(request,task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))	
	DepFormset=inlineformset_factory(Task, TaskDependency,fk_name='from_task',extra=1,can_delete=False)

	if request.method=='POST':
		new_dep=DepFormset(request.POST,instance=task)
		if new_dep.is_valid(): new_dep.save()
		return redirect('task_detail',task_id=task.id)
	else:
		params['deps']=TaskDependency.objects.filter(from_task=task)
		params['dep_formset']=DepFormset()
		return render_to_response('tj3/dep_edit.html',params)	
	
def period_edit(request,period_id):
	params={}
	params['period']=period=TaskPeriod.objects.filter(id=int(period_id))
	params['task']=Task.objects.get(id=period[0].task.id)
	PeriodFormset=modelformset_factory(TaskPeriod,exclude=('task',),extra=0)
	
	if request.method=='POST':
		new_period=PeriodFormset(request.POST)
		if request.POST['command'].lower()=='update' and new_period.is_valid():
			new_period.save()
		elif request.POST['command'].lower()=='delete':

			params['period'].delete()
		return redirect('task_detail',task_id=params['task'].id)
	else:
		params['period_formset']=PeriodFormset(queryset=period)
		
	return render_to_response('tj3/period_edit.html',params)
	
def period_create(request,task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))	
	PeriodFormset=inlineformset_factory(Task, TaskPeriod,extra=1,can_delete=False)

	if request.method=='POST':
		new_period=PeriodFormset(request.POST,instance=task)
		if new_period.is_valid(): new_period.save()	
		return redirect('task_detail',task_id=task.id)
	else:
		params['periods']=periods=TaskPeriod.objects.filter(task=task)
		params['period_formset']=PeriodFormset()
		return render_to_response('tj3/period_create.html',params)	

def task_detail(request, task_id):
	params={}
	params['task']=task=Task.objects.get(id=int(task_id))
	
	if request.method=='POST':
	
		if request.POST['command'].lower()=='create new period':
			return redirect('period_create',task_id=task.id)
		elif request.POST['command'].lower()=='create new dependency':
			return redirect('dep_create',task_id=task.id)
		elif request.POST['command'].lower()=='allocate resource':
			return redirect('resource_allocate',task_id=task.id)
		elif request.POST['command'].lower()=='task hiearchy':
			return redirect('task_move',proj_id=task.project.id, task_id=task.id)
		elif request.POST['command'].lower()=='update tji':
			tasks=Task.tree.filter(project=task.project)
			# dump to a file
			TaskDumpToFile(tasks, task.project.working_path)
			
			return redirect('task_detail',task_id=task_id)			
	else:
		params['periods']=TaskPeriod.objects.filter(task=task)
		params['deps']=TaskDependency.objects.filter(from_task=task)
		#params['resource_assigned'] = AllocateResource.objects.filter(task=task)
		resources = list(AllocateResource.objects.filter(task=task))
	
		# resources are inherited
		for a in task.get_ancestors():
			resources += list(AllocateResource.objects.filter(task=a))
		
		params['resource_assigned']=set(resources)
	
	return render_to_response('tj3/task_detail.html',params)

def journal_create(request,booking_id):
	params={}
	params['booking']=booking=Booking.objects.get(id=int(booking_id))	
	JournalFormset=inlineformset_factory(Booking, Journal,extra=1,can_delete=False)

	if request.method=='POST':
		new_journal=JournalFormset(request.POST,instance=booking)
		if new_journal.is_valid(): new_journal.save()	
		return redirect('booking_edit',booking_id=booking_id)
	else:
		params['journals']=Journal.objects.filter(booking=booking)
		params['journal_formset']=JournalFormset()
		return render_to_response('tj3/journal_create.html',params)	

def journal_edit (request, journal_id):
	params={}
	journal=Journal.objects.filter(id=int(journal_id))
	JournalFormset = modelformset_factory(Journal, exclude=('booking',),extra=0)
	if request.method=='POST':
		new_journal=JournalFormset(request.POST)
		if request.POST['command'].lower()=='update' and new_journal.is_valid():
			new_journal.save()
		elif request.POST['command'].lower()=='delete':
			journal=journal[0]
			journal.delete()
		return redirect('booking_edit',booking_id=journal[0].booking.id)
	else:
		params['journal_formset']=JournalFormset(queryset=journal)
	return render_to_response('tj3/journal_edit.html',params)	
	
def booking_edit (request, booking_id):
	params={}
	booking=Booking.objects.filter(id=int(booking_id))
	params['task']=Task.objects.get(id=booking[0].task.id)
	
	BookingFormset = modelformset_factory(Booking, exclude=('resource','task'), extra=0)
	if request.method=='POST':
		cmd=request.POST['command'].lower()
		new_booking=BookingFormset(request.POST)
		if cmd =='update' and new_booking.is_valid():
			new_booking.save()
		elif cmd=='delete':
			# If we are deleting this booking,
			# we are deleting all journals associated with this booking.
			# In most cases, booking-journal is 1-to-1.
			# But DB model allows N journal to 1 booking.
			#booking=booking[0]
			for j in Journal.objects.filter(booking=booking):
				j.delete()
			booking.delete()
		elif cmd=='add new journal':
			return redirect('journal_create',booking_id=booking_id)
		return redirect('task_detail', task_id=params['task'].id)
	else:
		params['booking']=booking[0]
		params['booking_formset']=BookingFormset(queryset=booking)
		params['journals']=Journal.objects.filter(booking=booking)
	return render_to_response('tj3/booking_edit.html',params)	

def booking_create(request, task_id, resource_id):
	params={}
	params['now']=datetime.today()
	params['task']=task=Task.objects.get(id=int(task_id))
	params['resource']=resource=Resource.objects.get(id=int(resource_id))
	
	BookingFormset=inlineformset_factory(Task, Booking,extra=1,can_delete=False)
	JournalFormset=inlineformset_factory(Booking, Journal,extra=1,can_delete=False)

	# get allocated resource
	if request.method=='POST':
		new_booking=BookingFormset(request.POST,instance=task)
		if new_booking.is_valid():
			booking=new_booking.save(commit=False)[0]
			booking.resource=resource

			# by doing this, I'm forcing user to create a journal for each booking!
			# this is a good business practice.
			new_journal=JournalFormset(request.POST,instance=booking)
			if new_journal.is_valid():
				booking.save()
				new_journal.save()
		return redirect('task_detail',task_id=task_id)				
	else:
		params['bookings']=bookings=Booking.objects.filter(task=task).filter(resource__exact=resource).order_by('created')
		params['booking_formset']=BookingFormset()
		
		params['journals']=journals=Journal.objects.filter(booking__in=bookings).order_by('created')	
		params['journal_formset']=JournalFormset()
	
	return render_to_response('tj3/booking_create.html',params)	
	
def calendar(request, year=2011, month=1):
	params={}
	
	tasks = Task.objects.order_by('created').filter(created__year=year, created__month=month)
	bookings=Booking.objects.filter(when__year=year, when__month=month)
	
	event_list=[]
	c = Calendar(6)
	for a in c.itermonthdays(int(year),int(month)):	
		t_list=[]
		j_list = []
		
		t_list=tasks.filter(created__day=a)
		b_list = bookings.filter(when__day=a)
		for b in b_list:
			for j in Journal.objects.filter(booking=b):
				j_list.append(j)
		event_list.append((a, t_list,j_list))
		
	params['events']=event_list
	params['last_year']=int(year)-1
	params['next_year']=int(year)+1
	params['year']=int(year)
	params['this_month']=int(month)
	params['month']={1:'Jan',2:'Feb',3:'March',4:'Apr',
					5:'May',6:'Jun',7:'Jul',8:'Aug',
					9:'Sep',10:'Oct',11:'Nov',12:'Dec'}
	return render_to_response('tj3/calendar.html', params)
  
def resource_display(request):
	params={}
	params['resources'] = []
	params['select_project']=ProjectSelectForm()
	
	if request.method=='POST':
		params['project']=proj=Project.objects.get(id=int(request.POST['project_list']))

		# back track from project->task->allocate resource->resource
		resources = []
		for t in Task.objects.filter(project=proj):
			for a in AllocateResource.objects.filter(task=t):
				resources.append(a.primary)
				if a.alternative: resources.append(a.alternative)				
		resources=list(set(resources))
		
		for r in resources:
			# worklimits
			worklimits=WorkLimit.objects.filter(resource=r)
			# timeoffs
			timeoffs = TimeOff.objects.filter(resource=r)
			# booking journals
			bookings= Booking.objects.filter(resource=r)
			journals=[]
			for j in [Journal.objects.filter(booking=b) for b in bookings]:
				for a in j:
					journals.append(a)
			
			# put everything together
			resource_info=(r,[],worklimits,timeoffs,bookings,journals)
			
			params['resources'].append(resource_info)
		
	return render_to_response('tj3/resource_display.html',params)
		
def journal_display(request):
	params={}
	params['select_project']=ProjectSelectForm()
	params['timeoffs']=TimeOff.objects.all()
	
	if request.method=='POST':
		params['project']=proj=Project.objects.get(id=int(request.POST['project_list']))
		
		# to get correct journals, we need to
		# project->task->booking->journal
		tasks=Task.tree.filter(project=proj)
		bookings=Booking.objects.filter(task__in=tasks).order_by('-when')
		
		# group by booking.when
		params['journals']=[]
		for key, group in groupby(bookings, lambda x: x.when):
			params['journals'].append((key,[Journal.objects.filter(booking=b) for b in group]))
	
		# dump to journal file
		journals=dict([(b,Journal.objects.filter(booking=b)) for b in bookings])
		JournalDumpToFile (proj, journals, dest=proj.working_path)

	return render_to_response('tj3/journal_display.html',params)

def journal_display_tji(request):
	params={}
	params['select_project']=ProjectSelectForm()
	params['timeoffs']=TimeOff.objects.all()
	
	if request.method=='POST':
		params['project']=proj=Project.objects.get(id=int(request.POST['project_list']))

		# to get correct journals, we need to
		# project->task->booking->journal
		tasks=Task.tree.filter(project=proj)
		bookings=Booking.objects.filter(task__in=tasks).order_by('-when')
		journals=dict([(b,Journal.objects.filter(booking=b)) for b in bookings])
		params['journals']=journals
		
		# dump to journal file
		JournalDumpToFile (proj, journals, dest=proj.working_path)
			
	return render_to_response('tj3/journal_display_tji.html',params)

def task_create(request):
	params={}
	TaskPeriodFormset=inlineformset_factory(Task, TaskPeriod,fk_name='task',extra=1,can_delete=False)
	TaskFormset=inlineformset_factory(Project, Task, fk_name='project',extra=1,can_delete=False)

	if request.method=='POST':
		project=Project.objects.get(id=int(request.POST['project_list']))

		# we create the new task
		new_task=TaskFormset(request.POST,instance=project)
		if new_task.is_valid():
			task=new_task.save(commit=False)[0]

			# set up task period
			new_task_period = TaskPeriodFormset(request.POST, instance=task)
			if new_task_period.is_valid():
				task.save()
				new_task_period.save()
				
				# save tags
				new_task.save_m2m()
			
		return redirect('task_detail', task_id=task.id)

	else:
		params['select_project']=ProjectSelectForm()
		params['task_period_formset']=TaskPeriodFormset()
		params['task_formset']=TaskFormset()

		return render_to_response('tj3/task_create.html',params)
			
def task_move(request, proj_id, task_id):
	params={}
	params['project']=proj=Project.objects.get(id=int(proj_id))
	params['target']=target=Task.objects.get(id=int(task_id))
	params['tasks'] = tasks = Task.tree.filter(project=proj)
	
	if request.method == 'POST':
		form = MoveNodeForm(target, request.POST)
		if form.is_valid():
			target = form.save()
	else:
		form= MoveNodeForm(target)
		
	params['move_form']= form
	return render_to_response('tj3/task_move.html',params)

def task_display_tjp(request):
	params={}
	params['select_project']=ProjectSelectForm()
	params['dependencies']=TaskDependency.objects.all()

	if request.method=='POST':
		params['project']=proj = Project.objects.get(id=int(request.POST['project_list']))
		params['tasks']=tasks=Task.tree.filter(project=proj)

		# dump to a file
		tji_file=TaskDumpToFile(tasks, proj.working_path)
		
		# read it back
		params['TJI']=open(tji_file,'r').read()
	return render_to_response('tj3/task_display_tjp.html',params)	
	
def task_display(request):
	params={}
	params['select_project']=ProjectSelectForm()

	if request.method=='POST':
		params['project']=proj = Project.objects.get(id=int(request.POST['project_list']))
		params['tasks']=tasks = Task.tree.filter(project=proj)
	
		# dump a DOT graph, haha
		diagram=TaskGraph(proj)
		
		TaskDumpToFile(tasks, proj.working_path)
		TaskDumpToFreemind(proj, tasks)
		
		#return HttpResponse(diagram,mimetype='application/png')
	return render_to_response('tj3/task_display.html',params,context_instance=RequestContext(request))

def home(request):
	return render_to_response('tj3/tj3.html')

def import_freemind (request):
	params = {}
	
	if request.method == 'POST':
		form = UploadFileForm(request.POST, request.FILES)
		if form.is_valid():
			ImportFreemind(request.FILES['file'])
			
			# do sth about the uploaded data
			return redirect('task_display')
		else:
			return redirect('home')
	else:
		params['form']=form = UploadFileForm()
	return render_to_response('tj3/import_freemind.html',params)

def export_freemind(request):
	params={}
	params['select_project']=ProjectSelectForm()
	if request.method=='POST':
		params['project']=proj = Project.objects.get(id=int(request.POST['project_list']))
		tasks = Task.tree.filter(project=proj).order_by('created')
		mm = TaskDumpToFreemind(proj, tasks)	
		return HttpResponse(mm,mimetype='application/octet-stream')
	else:
		return render_to_response('tj3/export_freemind.html',params,context_instance=RequestContext(request))

def import_mpp (request):
	params = {}
	
	if request.method == 'POST':
		form = UploadFileForm(request.POST, request.FILES)
		if form.is_valid():
			ImportMPP(request.FILES['file'])
			
			# do sth about the uploaded data
			return redirect('task_display')
		else:
			return redirect('home')
	else:
		params['form']=form = UploadFileForm()
	return render_to_response('tj3/import_mpp.html',params)
